#include "string.h"
#include<func.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/sha.h>
#include <time.h>

//#include"public.h"
typedef unsigned char BYTE; // 8-bit byte
typedef unsigned int WORD;  // 32-bit word

#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

static const BYTE PADDING[64] = { 0x80 };

static const WORD K[] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xb00327c8,
    0x98badcfe, 0xc04b73b3, 0x289b7ec6, 0x8b44f7af,
    0x8c1f2e5b, 0x6ca6351e, 0x41e4c6e0, 0x50c72440,
    0x2b96f0d9, 0x3e3eec7a, 0x5e25e79d, 0x59c59f2c,
    0x4c1f2e5b, 0x6ca6351e, 0x41e4c6e0, 0x50c72440,
    0x2b96f0d9, 0x3e3eec7a, 0x5e25e79d, 0x59c59f2c,
    0x4c1f2e5b, 0x6ca6351e, 0x41e4c6e0, 0x50c72440,
    0x2b96f0d9, 0x3e3eec7a, 0x5e25e79d, 0x59c59f2c
};

static const WORD INITIAL_STATE[4] = {
    0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
};

void md5_transform(WORD state[4], const BYTE block[64]) {
    WORD a = state[0];
    WORD b = state[1];
    WORD c = state[2];
    WORD d = state[3];
    WORD x[16];

    for (int i = 0; i < 16; i++) {
        x[i] = ((WORD) block[i * 4]) |
               (((WORD) block[i * 4 + 1]) << 8) |
               (((WORD) block[i * 4 + 2]) << 16) |
               (((WORD) block[i * 4 + 3]) << 24);
    }

    // Main MD5 algorithm
    // Round 1
    for (int i = 0; i < 64; i++) {
        WORD f, g;
        if (i < 16) {
            f = (b & c) | (~b & d);
            g = i;
        } else if (i < 32) {
            f = (d & b) | (~d & c);
            g = (5 * i + 1) % 16;
        } else if (i < 48) {
            f = b ^ c ^ d;
            g = (3 * i + 5) % 16;
        } else {
            f = c ^ (b | ~d);
            g = (7 * i) % 16;
        }
        WORD temp = d;
        d = c;
        c = b;
        b += ((a + f + K[i] + x[g]) << (i < 16 ? S11 : (i < 32 ? S21 : (i < 48 ? S31 : S41))) | 
                     ((a + f + K[i] + x[g]) >> (32 - (i < 16 ? S11 : (i < 32 ? S21 : (i < 48 ? S31 : S41))))));
        a = temp;
    }

    // Add the compressed chunk to the current state
    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
}

void md5(const BYTE *input, size_t length, BYTE output[16]) {
    WORD state[4];
    memcpy(state, INITIAL_STATE, sizeof(state));

    BYTE buffer[64];
    size_t buffer_index = 0;
    size_t bits_length = length * 8;

    for (size_t i = 0; i < length; i++) {
        buffer[buffer_index++] = input[i];
        if (buffer_index == 64) {
            md5_transform(state, buffer);
            buffer_index = 0;
        }
    }

    // Handle the final bytes
    size_t padding_size = (buffer_index < 56) ? (56 - buffer_index) : (64 + 56 - buffer_index);
    memcpy(buffer + buffer_index, PADDING, 1);
    memset(buffer + buffer_index + 1, 0, padding_size - 1);
    memcpy(buffer + 56, &bits_length, 4);
    memcpy(buffer + 60, &bits_length + 32, 4);

    md5_transform(state, buffer);
    memcpy(output, state, 16);
}

void print_md5(const BYTE *md5) {
    for (int i = 0; i < 16; i++) {
        printf("%02x", md5[i]);
    }
    printf("\n");
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return EXIT_FAILURE;
    }
    BYTE md5_result[16];
    BYTE buffer[1024];
    size_t bytes_read;

    FILE* file_fd = fopen(argv[1],"rb");
    while ((bytes_read = fread(buffer, 1, sizeof(buffer),file_fd)) > 0) {
        md5(buffer, bytes_read, md5_result);
    }

    fclose(file_fd);
    printf("MD5 hash of %s: ", argv[1]);
    print_md5(md5_result);

    return EXIT_SUCCESS;
}

